/*
*
* ████████╗░█████╗░███████╗██████╗░░█████╗░
* ╚══██╔══╝██╔══██╗██╔════╝██╔══██╗██╔══██╗
* ░░░██║░░░██║░░██║█████╗░░██████╦╝███████║
* ░░░██║░░░██║░░██║██╔══╝░░██╔══██╗██╔══██║
* ░░░██║░░░╚█████╔╝██║░░░░░██████╦╝██║░░██║
* ░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═════╝░╚═╝░░╚═╝
* 
*  :: 九方通逊-Henry ::   Copyright©2020 tofba.com  
* 
*/
package com.lmexpress.service.impl;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lmexpress.enums.ApiCredential;
import com.lmexpress.enums.ApiOrderError;
import com.lmexpress.orm.etower.Label;
import com.lmexpress.orm.etower.Order;
import com.lmexpress.orm.etower.OrderItem;
import com.lmexpress.orm.openapi.ExpressOrderApi;
import com.lmexpress.orm.openapi.OrderInvoice;
import com.lmexpress.orm.rest.ApiLabelResponse;
import com.lmexpress.orm.rest.ApiOrderResponse;
import com.lmexpress.service.ApiService;
import com.lmexpress.util.ApiUtil;
import com.lmexpress.util.HttpClientPoolUtil;
import com.lmexpress.util.HttpClientResult;

/**
 * 
 * <eTower 新西兰/澳洲.>
 * 
 * @author Henry(fba02)
 * @version [版本号, 2020-12-09]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class EtOwerClient implements ApiService {
	private Logger LOGGER = LoggerFactory.getLogger(EtOwerClient.class);
	private static final String SERVICE_URL = "https://cn.etowertech.com";// http://qa.etowertech.com
	private static final String ORDERS = "/services/shipper/orders";
	private static final String LABELS = "/services/shipper/labels";
	/**
	 * 不同客户需要修改成自己拥有的ACCESS_KEY，也就是api文档中说的token
	 */
	// public static final String ACCESS_KEY = "test5AdbzO5OEeOpvgAVXUFE0A";

	/**
	 * 不同客户需要修改成自己拥有的ACCESS_KEY，也就是api文档中说的key
	 */
	// public static final String SECRET_ACCESS_KEY = "79db9e5OEeOpvgAVXUFWSD";
	private static final char NEW_LINE = (char) 0x000A;
	private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
	private static final String HEADERS_AUTHORIZATION_PREFIX = "WallTech ";
	private static final String FAILURE = "Failure";
	// private static final String SUCCESS = "Success";
	private static final String CURRENCY_CODE = "AUD";
	private static final String WEIGHT_UNIT = "KG";

	@Override
	public ApiOrderResponse preProcessCreateOrder(ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public String mapping(ExpressOrderApi expressOrderApi) {
		Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
		Order order = new Order();
		order.setReferenceNo(expressOrderApi.getCustomerInvoiceCode());
		order.setServiceCode(apiCredentials.get(ApiCredential.ETOWER_SERVICE_CODE.getKey()));
		order.setBatteryPacking(apiCredentials.get(ApiCredential.ETOWER_BATTERY_PACKING.getKey()));
		order.setBatteryType(apiCredentials.get(ApiCredential.ETOWER_BATTERY_TYPE.getKey()));
//        if ("AU-P".equals(thirdPartyProductCode) || "NZ-P".equals(thirdPartyProductCode)) {
//            order.setBatteryPacking("No Battery");
//            order.setBatteryType("No Battery");
//        }
//        if ("AU-D".equals(thirdPartyProductCode) || "NZ-D".equals(thirdPartyProductCode)) {
//            order.setBatteryPacking("inside equipment");
//            order.setBatteryType("Lithium Ion Polymer");
//        }
		order.setInvoiceCurrency(StringUtils.isBlank(expressOrderApi.getCurrencyCode()) ? CURRENCY_CODE : expressOrderApi.getCurrencyCode());
		order.setRecipientName(expressOrderApi.getConsigneeName());
		order.setRecipientCompany(expressOrderApi.getConsigneeCompanyName());
		String phone = StringUtils.isBlank(expressOrderApi.getConsigneeMobile()) ? expressOrderApi.getConsigneeTelephone() : expressOrderApi.getConsigneeMobile();
		order.setPhone(phone);
		order.setEmail(expressOrderApi.getConsigneeEmail());
		order.setAddressLine1(expressOrderApi.getConsigneeAddress());
		order.setAddressLine2(expressOrderApi.getConsigneeAddress2());
		order.setCity(expressOrderApi.getConsigneeCity());
		order.setState(expressOrderApi.getConsigneeState());
		order.setPostcode(expressOrderApi.getConsigneePostCode());
		order.setCountry(expressOrderApi.getCountryCode());
		order.setShipperName(expressOrderApi.getShipperName());
		order.setShipperPhone(expressOrderApi.getShipperTelephone());
		order.setShipperCity(expressOrderApi.getShipperCity());
		order.setShipperState(expressOrderApi.getShipperState());
		order.setShipperPostcode(expressOrderApi.getShipperPostCode());
		order.setShipperCountry(expressOrderApi.getShipperCountry());
		order.setShipperAddressLine1(expressOrderApi.getShipperAddress1());
		order.setShipperAddressLine2(expressOrderApi.getShipperAddress2());
		order.setShipperAddressLine3(expressOrderApi.getShipperAddress3());
		OrderItem orderItem = null;
		List<OrderInvoice> orderInvoices = expressOrderApi.getOrderInvoices();
		Double weightDouble = 0D;
		Double totalDeclaredValue = 0D;
		if (CollectionUtils.isNotEmpty(orderInvoices)) {
			StringBuffer desBuffer = new StringBuffer();
			List<OrderItem> orderItems = new ArrayList<OrderItem>();
			for (OrderInvoice orderInvoice : orderInvoices) {
				orderItem = new OrderItem();
				orderItem.setSku(orderInvoice.getSkuCode());
				orderItem.setDescription(orderInvoice.getSkuNameEn());
				orderItem.setNativeDescription(orderInvoice.getSkuNameCn());
				orderItem.setHsCode(orderInvoice.getHsCode());
				orderItem.setOriginCountry(DEFAULT_SHIPPER_COUNTRY_CODE);
				orderItem.setWeight(orderInvoice.getInvoiceWeight());
				orderItem.setItemCount(orderInvoice.getInvoicePcs());
				orderItem.setUnitValue(new BigDecimal(orderInvoice.getInvoiceAmount()).divide(new BigDecimal(orderInvoice.getInvoicePcs()), 4, RoundingMode.HALF_UP).doubleValue());
				orderItem.setWeight(orderInvoice.getInvoiceWeight());
				weightDouble += (orderInvoice.getInvoiceWeight() == null ? 0D : orderInvoice.getInvoiceWeight());
				totalDeclaredValue += (orderInvoice.getInvoiceAmount() == null ? 0D : orderInvoice.getInvoiceAmount());
				orderItems.add(orderItem);
				desBuffer.append(orderInvoice.getSkuNameCn()).append(";");
			}
			if (null == expressOrderApi.getWeight()) {
				expressOrderApi.setWeight(weightDouble);
			}
			order.setOrderItems(orderItems);
			order.setInvoiceValue(totalDeclaredValue);
			order.setNativeDescription(desBuffer.toString());
			order.setDescription(orderInvoices.get(0).getSkuNameEn());
		}
		order.setWeight(expressOrderApi.getWeight());
		order.setWeightUnit(WEIGHT_UNIT);
		List<Order> orders = new ArrayList<Order>();
		orders.add(order);
		return new Gson().toJson(orders);
	}

	@Override
	public ApiOrderResponse createOrder(ExpressOrderApi expressOrderApi) {
		String orderJson = mapping(expressOrderApi);
		LOGGER.info("etower.shipment.request.json:{}", orderJson);
		Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
		Map<String, String> headers = addAuthHeader("POST", apiCredentials.get(ApiCredential.ETOWER_ACCESS_KEY.getKey()), apiCredentials.get(ApiCredential.ETOWER_SECRET_ACCESS_KEY.getKey()), SERVICE_URL, ORDERS);
		try {
			HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(SERVICE_URL + ORDERS, headers, orderJson);
			if (null == httpClientResult) {
				return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
			}
			String responseJson = httpClientResult.getContent();
			LOGGER.info("etower.shipment.response.result:{}", responseJson);
			if (StringUtils.isBlank(responseJson)) {
				return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
			}
			Map<String, Object> responseMap = new Gson().fromJson(responseJson, new TypeToken<Map<String, Object>>() {
			}.getType());
			String status = (String) responseMap.get("status");
			if (FAILURE.equalsIgnoreCase(status)) {
				@SuppressWarnings("unchecked")
				List<Map<String, Object>> errors = (List<Map<String, Object>>) responseMap.get("errors");
				if (CollectionUtils.isNotEmpty(errors)) {
					StringBuffer errorBuffer = new StringBuffer();
					for (Map<String, Object> errorMap : errors) {
						errorBuffer.append(errorMap.get("message")).append(";");
					}
					return new ApiOrderResponse(false, null, errorBuffer.toString(), false, null);
				}
			}
			// {"status":"Success","errors":null,"data":[{"status":"Success","errors":null,"orderId":"9Z893q1vuKxtRfROWhQ-UQ","referenceNo":"J0418US202011161128008","trackingNo":"JDQ002469601000935107","connoteId":null}]}
			@SuppressWarnings("unchecked")
			List<Map<String, Object>> data = (List<Map<String, Object>>) responseMap.get("data");
			String orderId = (String) data.get(0).get("orderId");
			String trackingNo = (String) data.get(0).get("trackingNo");
			ApiOrderResponse apiOrderResponse = new ApiOrderResponse();
			apiOrderResponse.setSuccess(true);
			apiOrderResponse.setThirdPartyorderNo(orderId);
			apiOrderResponse.setTrackNumber(trackingNo);
			ApiLabelResponse apiLabelResponse = label(trackingNo, apiCredentials);
			if (StringUtils.isNotBlank(apiLabelResponse.getLabelPath())) {
				apiOrderResponse.setLabelUrl(apiLabelResponse.getLabelPath());// 远程面单路径
			}
			if (StringUtils.isNotBlank(apiLabelResponse.getRelativePath())) {
				apiOrderResponse.setLmLabelUrl(apiLabelResponse.getRelativePath());// 本地服务器路径
			}
			return apiOrderResponse;
		} catch (Exception e) {
			LOGGER.error("etower.shipment.exception", e);
			return new ApiOrderResponse(false, null, ApiOrderError.CREATE_ORDER_EXCEPTION.getMessage(), true, null);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public ApiLabelResponse label(String trackingNumber, Map<String, String> pipelineMap) {
		Map<String, String> headers = addAuthHeader("POST", pipelineMap.get(ApiCredential.ETOWER_ACCESS_KEY.getKey()), pipelineMap.get(ApiCredential.ETOWER_SECRET_ACCESS_KEY.getKey()), SERVICE_URL, LABELS);
		List<String> orderIds = new ArrayList<String>();
		orderIds.add(trackingNumber);
		Label label = new Label();
		label.setOrderIds(orderIds);
		Gson gson = new Gson();
		String requestJson = gson.toJson(label);
		LOGGER.info("etower.label.request.json:{}", requestJson);
		try {
			HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(SERVICE_URL + LABELS, headers, requestJson);
			if (null == httpClientResult) {
				return new ApiLabelResponse(trackingNumber, null);
			}
			String responseJson = httpClientResult.getContent();
			LOGGER.info("etower.label.response.result:{}", responseJson);
			if (StringUtils.isBlank(responseJson)) {
				return new ApiLabelResponse(trackingNumber, null);
			}
			Map<String, Object> responseMap = gson.fromJson(responseJson, new TypeToken<Map<String, Object>>() {
			}.getType());
			String status = (String) responseMap.get("status");
			if (FAILURE.equalsIgnoreCase(status)) {
				return new ApiLabelResponse(trackingNumber, null);
			}
			List<Map<String, Object>> data = (List<Map<String, Object>>) responseMap.get("data");
			String labelContent = (String) data.get(0).get("labelContent");
			if (StringUtils.isBlank(labelContent)) {
				labelContent = (String) data.get(0).get("barCodeImage");
			}
			String pdfName = trackingNumber + ".pdf";
			String centrePath = ApiUtil.getCurrentDay() + File.separator;
			String relativePath = DEFAULT_LABEL_FOLDER + File.separator + centrePath;
			String folderPath = DEFAULT_LABEL_ROOT + relativePath;
			if (!new File(folderPath).exists()) {
				new File(folderPath).mkdirs();
			}
			String fileUrl = folderPath + pdfName;
			ApiLabelResponse apiLabelResponse = new ApiLabelResponse();
			try {
				ApiUtil.decoderBase64File(labelContent, fileUrl);
			} catch (Exception e) {
				LOGGER.error("etower.decoder.label.error", e);
			}
			apiLabelResponse.setBarCode(trackingNumber);
			apiLabelResponse.setBase64File(labelContent);
			apiLabelResponse.setRelativePath(relativePath + pdfName);
			apiLabelResponse.setLabelPath(relativePath + pdfName);
			return apiLabelResponse;
		} catch (Exception e) {
			LOGGER.error("etower.label.exception:", e);
			return new ApiLabelResponse(trackingNumber, null);
		}
	}

	@Override
	public ApiLabelResponse label(String referenceNo, String trackNumber, Map<String, String> configure) {
		return null;
	}

	@Override
	public ApiLabelResponse labelRender(String trackNumber, ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public void tracking(String trackNumber, Map<String, String> configure) {
	}

	@Override
	public String trackNumber(String code, Map<String, String> configure) {
		return null;
	}

	/**
	 * <服务请求发送时间格式化.>
	 * 
	 * @param dateTime
	 * @return
	 */
	private static String formatDateTime(Date dateTime) {
		if (dateTime == null) {
			return null;
		}
		SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
		format.setTimeZone(TimeZone.getTimeZone("GMT"));
		return format.format(dateTime);
	}

	/**
	 * 
	 * <签名计算.>
	 * 
	 * @param secretAccessKey
	 * @param data
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, 2020-11-16]
	 * @see [类、类#方法、类#成员]
	 */
	private static String calculate(String secretAccessKey, String data) {
		try {
			// secretKeySpec 对key进行HmacSHA1加密
			SecretKeySpec signingKey = new SecretKeySpec(secretAccessKey.getBytes("UTF-8"), HMAC_SHA1_ALGORITHM);
			// 初始化一个HmacSHA1算法的mac对象
			Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
			// 使用加密后的secretKeySpec对象，对mac进行初始化
			mac.init(signingKey);
			// 完成mac操作，获取byte
			byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
			// 将完成操作的mac进行base64转换
			return new String(Base64.getEncoder().encode(rawHmac), "UTF-8");
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * 
	 * <构造请求头.>
	 * 
	 * @param method
	 * @param accessKey
	 * @param secretAccessKey
	 * @param baseUrl
	 * @param path
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, 2020-11-16]
	 * @see [类、类#方法、类#成员]
	 */
	public static Map<String, String> addAuthHeader(String method, String accessKey, String secretAccessKey, String baseUrl, String path) {
		String date = formatDateTime(new Date());
		StringBuilder sb = new StringBuilder();
		sb.append(method).append(NEW_LINE);
		sb.append(date).append(NEW_LINE);
		sb.append(baseUrl + path);
		String authorization = MessageFormat.format(HEADERS_AUTHORIZATION_PREFIX + "{0}:{1}", accessKey, calculate(secretAccessKey, sb.toString()));
		Map<String, String> authHeader = new HashMap<String, String>();
		authHeader.put("X-WallTech-Date", date);
		authHeader.put("Authorization", authorization);
		authHeader.put("Content-Type", "application/json");
		authHeader.put("Accept", "application/json");
		return authHeader;
	}

	public String replaceParameters(String json, String parameter, String value) {
		return json.replaceAll(parameter, value);
	}
}